#include <jni.h>
#include <string>
#include <math.h>

#ifdef ANDROID
#include <jni.h>
#include <android/log.h>
#define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, "xohnffmpeg", format, ##__VA_ARGS__)
#define LOGI(format, ...)  __android_log_print(ANDROID_LOG_INFO,  "xohnffmpeg", format, ##__VA_ARGS__)
#else
#define LOGE(format, ...)  printf("xohnffmpeg" format "\n", ##__VA_ARGS__)
#define LOGI(format, ...)  printf("xohnffmpeg " format "\n", ##__VA_ARGS__)
#endif


/**
 * Split Left and Right channel of 16LE PCM file.
 * @param jstr_url  Location of PCM file.
 * 分离PCM16LE双声道音频采样数据的左声道和右声道
 * PCM16LE双声道数据中左声道和右声道的采样值是间隔存储的。每个采样值占用2Byte空间
 * 例程中声音样值的采样频率一律是44100Hz，采样格式一律为16LE。“16”代表采样位数是16bit。由于1Byte=8bit，所以一个声道的一个采样值占用2Byte。“LE”代表Little Endian，代表2 Byte采样值的存储方式为高位存在高地址中
 */
extern "C" JNIEXPORT jint JNICALL
Java_com_xohn_ffmpeg_AVUtils_pcm16leSplit(
        JNIEnv *env,jobject /* this */,
        jstring jstr_url) {
	char input_url[100] = {0};
	const char *str = env->GetStringUTFChars(jstr_url, NULL);
	sprintf(input_url,"%s",str);
	env->ReleaseStringUTFChars(jstr_url,str);

	FILE *fp = fopen(input_url,"rb+");
	if(fp == NULL) return -1;

	strcpy(input_url+strlen(input_url)-4,"_l.pcm");
	FILE *fp1 = fopen(input_url,"wb+");
	strcpy(input_url+strlen(input_url)-5,"r.pcm");
	FILE *fp2 = fopen(input_url,"wb+");
 
	unsigned char *sample = (unsigned char *)malloc(4);
 
	while(!feof(fp)){
		fread(sample,1,4,fp);
		//L
		fwrite(sample,1,2,fp1);
		//R
		fwrite(sample+2,1,2,fp2);
	}
 
	free(sample);
	fclose(fp);
	fclose(fp1);
	fclose(fp2);

	return 0;
}


/**
 * Halve volume of Left channel of 16LE PCM file
 * @param jstr_url  Location of PCM file.
 * 将PCM16LE双声道音频采样数据中左声道的音量降一半
 * 读出左声道的2 Byte的取样值之后，将其当成了C语言中的一个short类型的变量。将该数值除以2之后写回到了PCM文件中
 */
extern "C" JNIEXPORT jint JNICALL
Java_com_xohn_ffmpeg_AVUtils_pcm16leHalfVolLeft(
		JNIEnv *env,jobject /* this */,
		jstring jstr_url) {
	char input_url[100] = {0};
	const char *str = env->GetStringUTFChars(jstr_url, NULL);
	sprintf(input_url,"%s",str);
	env->ReleaseStringUTFChars(jstr_url,str);

	FILE *fp = fopen(input_url,"rb+");
	if(fp == NULL) return -1;

	strcpy(input_url+strlen(input_url)-4,"_halfvolleft.pcm");
	FILE *fp1 = fopen(input_url,"wb+");

	unsigned char *sample = (unsigned char *)malloc(4);

	while(!feof(fp)){
		short *samplenum = NULL;
		fread(sample,1,4,fp);

		samplenum = (short *)sample;
		*samplenum = *samplenum/2;
		//L
		fwrite(sample,1,2,fp1);
		//R
		fwrite(sample+2,1,2,fp1);
	}

	free(sample);
	fclose(fp);
	fclose(fp1);

	return 0;
}


/**
 * Re-sample to double the speed of 16LE PCM file
 * @param jstr_url  Location of PCM file.
 * 将PCM16LE双声道音频采样数据的声音速度提高一倍
 * 只采样了每个声道奇数点的样值。处理完成后，原本22秒左右的音频变成了11秒左右。音频的播放速度提高了2倍，音频的音调也变高了很多
 */
extern "C" JNIEXPORT jint JNICALL
Java_com_xohn_ffmpeg_AVUtils_pcm16leDoubleSpeed(
		JNIEnv *env,jobject /* this */,
		jstring jstr_url) {
	char input_url[100] = {0};
	const char *str = env->GetStringUTFChars(jstr_url, NULL);
	sprintf(input_url,"%s",str);
	env->ReleaseStringUTFChars(jstr_url,str);

	FILE *fp = fopen(input_url,"rb+");
	if(fp == NULL) return -1;

	strcpy(input_url+strlen(input_url)-4,"_doublespeed.pcm");
	FILE *fp1 = fopen(input_url,"wb+");

	int cnt = 0;

	unsigned char *sample = (unsigned char *)malloc(4);

	while(!feof(fp)){

		fread(sample,1,4,fp);

		if(cnt%2 != 0){
			//L
			fwrite(sample,1,2,fp1);
			//R
			fwrite(sample+2,1,2,fp1);
		}
		cnt++;
	}
	LOGI("Sample Cnt:%d\n",cnt);

	free(sample);
	fclose(fp);
	fclose(fp1);

	return 0;
}


/**
 * Convert PCM-16 data to PCM-8 data.
 * @param jstr_url  Location of PCM file.
 * 将PCM16LE双声道音频采样数据转换为PCM8音频采样数据
 * PCM16LE格式的采样数据的取值范围是-32768到32767，而PCM8格式的采样数据的取值范围是0到255
 * 所以PCM16LE转换到PCM8需要经过两个步骤：
 * 第一步是将-32768到32767的16bit有符号数值转换为-128到127的8bit有符号数值
 * 第二步是将-128到127的8bit有符号数值转换为0到255的8bit无符号数值
 */
extern "C" JNIEXPORT jint JNICALL
Java_com_xohn_ffmpeg_AVUtils_pcm16leToPcm8(
		JNIEnv *env,jobject /* this */,
		jstring jstr_url) {
	char input_url[100] = {0};
	const char *str = env->GetStringUTFChars(jstr_url, NULL);
	sprintf(input_url,"%s",str);
	env->ReleaseStringUTFChars(jstr_url,str);

	FILE *fp = fopen(input_url,"rb+");
	if(fp == NULL) return -1;

	strcpy(input_url+strlen(input_url)-4,"_to8.pcm");
	FILE *fp1 = fopen(input_url,"wb+");

	unsigned char *sample = (unsigned char *)malloc(4);

	while(!feof(fp)){

		short *samplenum16 = NULL;
		char samplenum8 = 0;
		unsigned char samplenum8_u = 0;
		fread(sample,1,4,fp);
		//(-32768-32767)
		samplenum16 = (short *)sample;
		samplenum8 = (*samplenum16)>>8;
		//(0-255)
		samplenum8_u = samplenum8+128;
		//L
		fwrite(&samplenum8_u,1,1,fp1);

		samplenum16 = (short *)(sample+2);
		samplenum8 = (*samplenum16)>>8;
		samplenum8_u = samplenum8+128;
		//R
		fwrite(&samplenum8_u,1,1,fp1);
	}

	free(sample);
	fclose(fp);
	fclose(fp1);

	return 0;
}


/**
 * Cut a 16LE PCM single channel file.
 * @param jstr_url        Location of PCM file.
 * @param start_num  start point
 * @param dur_num    how much point to cut
 * 将从PCM16LE单声道音频采样数据中截取一部分数据
 * 从PCM数据中选取一段采样值保存下来，并且输出这些采样值的数值
 */
extern "C" JNIEXPORT jint JNICALL
Java_com_xohn_ffmpeg_AVUtils_pcm16leCutSingleChannel(
		JNIEnv *env,jobject /* this */,
		jstring jstr_url,jint start_num,jint dur_num) {
	char input_url[100] = {0};
	const char *str = env->GetStringUTFChars(jstr_url, NULL);
	sprintf(input_url,"%s",str);
	env->ReleaseStringUTFChars(jstr_url,str);

	FILE *fp = fopen(input_url,"rb+");
	if(fp == NULL) return -1;

	strcpy(input_url+strlen(input_url)-4,"_cut.pcm");
	FILE *fp1 = fopen(input_url,"wb+");
	strcpy(input_url+strlen(input_url)-3,"txt");
	FILE *fp_stat = fopen(input_url,"wb+");

	unsigned char *sample = (unsigned char *)malloc(2);

	int cnt = 0;
	while(!feof(fp)){
		fread(sample,1,2,fp);
		if(cnt>start_num && cnt<=(start_num+dur_num)){
			fwrite(sample,1,2,fp1);

			short samplenum = sample[1];
			samplenum = samplenum*256;
			samplenum = samplenum+sample[0];

			fprintf(fp_stat,"%6d,",samplenum);
			if(cnt%10 == 0)
				fprintf(fp_stat,"\n",samplenum);
		}
		cnt++;
	}

	free(sample);
	fclose(fp);
	fclose(fp1);
	fclose(fp_stat);

	return 0;
}


/**
 * Convert PCM16LE raw data to WAVE format
 * @param jstr_url_in      Input PCM file.
 * @param channels     Channel number of PCM file.
 * @param sample_rate  Sample rate of PCM file.
 * @param jstr_url_out     Output WAVE file.
 * 将PCM16LE双声道音频采样数据转换为WAVE格式音频数据
 * 该格式的实质就是在PCM文件的前面加了一个文件头
 * WAVE文件是一种RIFF格式的文件。其基本块名称是“WAVE”，其中包含了两个子块“fmt”和“data”。从编程的角度简单说来就是由WAVE_HEADER、WAVE_FMT、WAVE_DATA、采样数据共4个部分组成。它的结构如下所示。
 * WAVE_HEADER
 * WAVE_FMT
 * WAVE_DATA
 * PCM数据
 * 在写入WAVE文件头的时候给其中的每个字段赋上合适的值就可以了
 * 但是有一点需要注意：WAVE_HEADER和WAVE_DATA中包含了一个文件长度信息的dwSize字段，该字段的值必须在写入完音频采样数据之后才能获得。因此这两个结构体最后才写入WAVE文件中。
 * 注：原始声明的数据类型unsigned long在有些C编译器上是64位的，这时候要改成unsigned int才可以，否则wav头有88bytes，标准的是44bytes
 */
extern "C" JNIEXPORT jint JNICALL
Java_com_xohn_ffmpeg_AVUtils_pcm16leToWave(
		JNIEnv *env,jobject /* this */,
		jstring jstr_url_in,jint channels,jint sample_rate,jstring jstr_url_out) {
	FILE   *fp,*fpout;
	const char *pcmpath = env->GetStringUTFChars(jstr_url_in, NULL);
	fp = fopen(pcmpath, "rb");
	env->ReleaseStringUTFChars(jstr_url_in,pcmpath);
	if(fp == NULL) {
		return -1;
	}

	const char *wavepath = env->GetStringUTFChars(jstr_url_out, NULL);
	fpout = fopen(wavepath, "wb+");
	env->ReleaseStringUTFChars(jstr_url_out,wavepath);
	if(fpout == NULL) {
		return -1;
	}


	typedef struct WAVE_HEADER{
		char         fccID[4];
		unsigned   int    dwSize;
		char         fccType[4];
	}WAVE_HEADER;
	LOGI("WAVE_HEADER size : %d", sizeof(WAVE_HEADER));

	typedef struct WAVE_FMT{
		char         fccID[4];
		unsigned   int       dwSize;
		unsigned   short     wFormatTag;
		unsigned   short     wChannels;
		unsigned   int       dwSamplesPerSec;
		unsigned   int       dwAvgBytesPerSec;
		unsigned   short     wBlockAlign;
		unsigned   short     uiBitsPerSample;
	}WAVE_FMT;
	LOGI("WAVE_HEADER size : %d", sizeof(WAVE_FMT));

	typedef struct WAVE_DATA{
		char       fccID[4];
		unsigned int dwSize;
	}WAVE_DATA;
	LOGI("WAVE_HEADER size : %d", sizeof(WAVE_DATA));

	if(channels==0 || sample_rate==0){
		channels = 2;
		sample_rate = 44100;
	}
	int bits = 16;

	WAVE_HEADER   pcmHEADER;
	WAVE_FMT   pcmFMT;
	WAVE_DATA   pcmDATA;

	unsigned   short   m_pcmData;

	//WAVE_HEADER
	memcpy(pcmHEADER.fccID,"RIFF",strlen("RIFF"));
	memcpy(pcmHEADER.fccType,"WAVE",strlen("WAVE"));
	fseek(fpout,sizeof(WAVE_HEADER),SEEK_CUR);
	//WAVE_FMT
	pcmFMT.dwSamplesPerSec = sample_rate;
	pcmFMT.dwAvgBytesPerSec = pcmFMT.dwSamplesPerSec*sizeof(m_pcmData);
	pcmFMT.uiBitsPerSample = bits;
	memcpy(pcmFMT.fccID,"fmt ",strlen("fmt "));
	pcmFMT.dwSize = 16;
	pcmFMT.wBlockAlign = 2;
	pcmFMT.wChannels = channels;
	pcmFMT.wFormatTag = 1;

	fwrite(&pcmFMT,sizeof(WAVE_FMT),1,fpout);

	//WAVE_DATA;
	memcpy(pcmDATA.fccID,"data",strlen("data"));
	pcmDATA.dwSize = 0;
	fseek(fpout,sizeof(WAVE_DATA),SEEK_CUR);

	fread(&m_pcmData,sizeof(unsigned short),1,fp);
	//这里先读，读完判断再写入比上面几个函数读写方法更合理
	while(!feof(fp)){
		pcmDATA.dwSize+=2;
		fwrite(&m_pcmData,sizeof(unsigned short),1,fpout);
		fread(&m_pcmData,sizeof(unsigned short),1,fp);
	}

	pcmHEADER.dwSize = 44+pcmDATA.dwSize;

	rewind(fpout);
	fwrite(&pcmHEADER,sizeof(WAVE_HEADER),1,fpout);
	fseek(fpout,sizeof(WAVE_FMT),SEEK_CUR);
	fwrite(&pcmDATA,sizeof(WAVE_DATA),1,fpout);

	fclose(fp);
	fclose(fpout);

	return 0;
}
